package com.logicbus.backend.message;

import com.alogic.idnote.IdNote;
import com.alogic.idnote.IdNoteFactory;
import com.alogic.idnote.IdNoteGroup;
import com.anysoft.util.IOTools;
import com.anysoft.util.JsonTools;
import com.anysoft.util.PropertiesConstants;
import com.anysoft.util.Settings;
import com.anysoft.util.compress.Compressor;
import com.anysoft.util.compress.compressor.GZIP;
import com.jayway.jsonpath.JsonPath;
import com.logicbus.backend.Context;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 基于YAML的Message
 *
 * @since 1.6.12.27 [20190403 duanyy] <br>
 *
 * @version 1.6.13.21 [20201021 duanyy] <br>
 * - 支持gzip压缩 <br>
 */
public class YamlMessage implements Message {
    protected static final Logger logger = LoggerFactory.getLogger(JsonMessage.class);

    protected static String formContentType = "application/x-www-form-urlencoded";
    protected static IdNoteGroup idnote = null;
    protected static boolean hostMask = true;
    protected static ThreadLocal<Yaml> yamlLocal = new ThreadLocal<Yaml>(){
        protected Yaml initialValue() {
            return new Yaml();
        };
    };
    private static Compressor gzipCompressor = new GZIP();
    private static boolean gzipSupport = false;
    /**
     * gzip开启的报文长度
     */
    private static int gzipEnableLength = 1024;
    static {
        gzipSupport = PropertiesConstants.getBoolean(Settings.get(),"http.yaml.gzip",gzipSupport);
        gzipEnableLength = PropertiesConstants.getInt(Settings.get(),"http.gzip.length",gzipEnableLength);
        formContentType = Settings.get().GetValue("http.formContentType",
                "application/x-www-form-urlencoded");
        hostMask = PropertiesConstants.getBoolean(Settings.get(),"servant.hostmask",hostMask);
        idnote = IdNoteFactory.get(PropertiesConstants.getString(Settings.get(), "servant.code.group", "servant.code"));
    }

    private String contentType = "text/plain;charset=utf-8";

    /**
     * Json结构的根节点
     */
    protected Map<String,Object> root = null;

    private long contentLength = 0;

    private String outputPath = "";

    private boolean isGzipEnable(Context ctx,String header){
        if (!gzipSupport) return false;
        String gzip = ctx.getRequestHeader(header);
        return StringUtils.isNotEmpty(gzip)?gzip.equalsIgnoreCase("gzip"):false;
    }

    public void setOutputPath(String path){
        this.outputPath = path;
    }

    protected static String getHost(String host){
        return hostMask ? host.replaceFirst("\\d{1,}\\.\\d{1,}\\.\\d{1,}\\.","*.*.*."):host;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void init(Context ctx) {
        boolean gzipEnable = isGzipEnable(ctx,"Content-Encoding");
        String data = null;
        {
            byte [] inputData = ctx.getRequestRaw();
            if (inputData != null){
                try {
                    if (gzipEnable && inputData.length > 0){
                        inputData = gzipCompressor.decompress(inputData);
                    }
                    data = new String(inputData,ctx.getEncoding());
                }catch (Exception ex){

                }
            }
        }

        if (data == null){
            //当客户端通过form来post的时候，Message不去读取输入流。
            String _contentType = ctx.getRequestContentType();
            if (_contentType == null || !_contentType.startsWith(formContentType)){
                InputStream in = null;
                try {
                    in = ctx.getInputStream();
                    if (gzipEnable && in.available() > 0){
                        in = gzipCompressor.getInputStream(in);
                    }
                    data = Context.readFromInputStream(in, ctx.getEncoding());
                }catch (Exception ex){
                    logger.error("Error when reading data from inputstream",ex);
                }finally{
                    IOTools.close(in);
                }
            }
        }

        if (data != null && data.length() > 0){
            contentLength += data.getBytes().length;
            try {
                Object rootObj = yamlLocal.get().load(data);
                if (rootObj instanceof Map){
                    root = (Map<String,Object>)rootObj;
                }
            }catch (Exception ex){
                logger.error("Failed to parse yaml document:" + data);
            }

        }
        if (root == null){
            root = new HashMap<String,Object>();
        }

        contentType = "text/plain;charset=" + ctx.getEncoding();
    }

    @Override
    public void finish(Context ctx,boolean closeStream) {
        boolean gzipEnable = isGzipEnable(ctx,"Accept-Encoding");
        Map<String,Object> _root = getRoot();
        JsonTools.setString(_root, "code", ctx.getReturnCode());
        if (idnote != null){
            IdNote note = idnote.getNote(ctx.getReturnCode());
            if (note != null){
                JsonTools.setString(_root, "reason", note.getNote());
                JsonTools.setString(_root, "advise", ctx.getReason());
            }else{
                JsonTools.setString(_root, "reason", ctx.getReason());
            }
        }else{
            JsonTools.setString(_root, "reason", ctx.getReason());
        }
        JsonTools.setString(_root, "duration", String.valueOf(ctx.getDuration()));
        JsonTools.setString(_root, "host", getHost(ctx.getHost()));
        JsonTools.setString(_root, "serial", ctx.getGlobalSerial());

        OutputStream out = null;
        try {

            Object outputObject = _root;

            if (StringUtils.isNotEmpty(outputPath)){
                try {
                    outputObject = JsonPath.read(outputObject, outputPath);
                }catch (Exception ex){
                    logger.error("Can not location jsonpath " + outputPath);
                }
            }

            outputObject = outputObject == null ? _root : outputObject;
            Yaml yaml = yamlLocal.get();
            String data = yaml.dump(outputObject);

            out = ctx.getOutputStream();
            ctx.setResponseContentType(contentType);
            byte [] bytes = data.getBytes(ctx.getEncoding());
            if (gzipEnable && bytes.length > gzipEnableLength){
                bytes = gzipCompressor.compress(bytes);
                ctx.setResponseHeader("Content-Encoding","gzip");
            }
            contentLength += bytes.length;
            ctx.setResponseContentLength(bytes.length);
            Context.writeToOutpuStream(out, bytes);
            out.flush();
        }catch (Exception ex){
            logger.error("Error when writing data to outputstream",ex);
        }finally{
            if (closeStream)
                IOTools.close(out);
        }
    }

    /**
     * 获取JSON结构的根节点
     *
     * @return JSON结构的根节点
     */
    public Map<String,Object> getRoot(){
        return root;
    }

    @Override
    public String toString(){
        return yamlLocal.get().dump(root);
    }

    @Override
    public String getContentType() {
        return contentType;
    }

    @Override
    public long getContentLength() {
        return contentLength;
    }
}